package io;

import info.Conference;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Content;
import org.jdom2.DataConversionException;
import org.jdom2.Element;
import org.jdom2.JDOMException;

import ui.MainPage;
import ui.Standards;
import users.Author;
import users.ProgramChair;
import users.Reviewer;
import users.Role;
import users.SubprogramChair;

/**
 * This class provides access to the Role data for the peer review system.
 * 
 * @author Aaron Chen
 * 
 * @version March 9, 2014
 * 
 */
public class RoleAccess extends AbstractAccess {

  /** The name of the XML file. */
  private static final String XML_FILE = "Role.xml";
  
  /** The name of the identification number attribute in the XML file. */
  private static final String ATTRIBUTE_ID = "id";
  /** The name of the role identification number attribute in the XML file. */
  private static final String ATTRIBUTE_ROLE_ID = "roleid";
  /** The name of the ID element in the XML file. */
  private static final String NODE_ID = "RoleID";
//  /** The name of the manuscript ID element in the XML file. */
//  private static final String NODE_MANUSCRIPT_ID = "ManuID";
  /** The name of the reviewer ID element in the XML file. */
  private static final String NODE_REVIEWER_ID = "ReviewerID";
  /** The name of the subprogram chair ID element in the XML file. */
  private static final String NODE_SPC_ID = "spcID";
  /** The name of the program chair ID element in the XML file. */
  private static final String NODE_PC_ID = "pcID";
  /** The name of the subprogram chair macro element in the XML file. */
  private static final String NODE_SPC = "SubprogramChairs";
//  /** The name of the manuscript macro element in the XML file. */
//  private static final String NODE_MANUSCRIPT = "Manuscripts";
  /** The name of the reviewer macro element in the XML file. */
  private static final String NODE_REVIEWER = "Reviewer";
  /** The name of the user ID element in the XML file. */
  private static final String NODE_USER_ID = "userID";
  /** The name of the conference ID element in the XML file. */
  private static final String NODE_CONFERENCE_ID = "confID";

  /**
   * The constructor to instantiate the RoleAccess objects.
   * @throws IOException If there is a problem establishing connectivity to the file.
   * @throws JDOMException If the file cannot be correctly parsed.
   */
  public RoleAccess() throws IOException, JDOMException {
    super(XML_FILE);
  }
  


  /**
   * Returns a specific Role.
   * 
   * @param the_id The identification number unique to each Role.
   * @return A single Role.
   */
  public Role getRole(final long the_id) {
    Role r = null;
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren(NODE_ID);
    for (Element e : list) {
      try {
        if (e.getAttribute(ATTRIBUTE_ID).getLongValue() == the_id) {
          r = dataToRole(e);
          break;
        }
      } catch (final DataConversionException err) {
        err.printStackTrace();
      }
    }
    return r;
  }

  /**
   * Writes a new Role entry into the database.
   * 
   * @param the_r A new Role.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean createRole(final Role the_r) {
    boolean result = true;

    final Element rootnode = getDoc().getRootElement();
    final Element e = roleToData(the_r, false);
    rootnode.addContent(e);
    
    

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    result = writeToDatabase();
    if (!result) {
      e.detach();
    }

    return result;
  }

  /**
   * Writes over a pre-existing role in the database.
   * 
   * @param the_r The Role data to write into the database.
   * @return true if the role to edit was found and overwritten; false
   *         otherwise.
   */
  public boolean editRole(final Role the_r) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element old_element = null;
    final Element new_element = roleToData(the_r, true);
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_r.getID()) {
        old_element = e.detach();
        rootnode.addContent(new_element);
        result = true;
        break;
      }
    }

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(old_element);
        new_element.detach();
      }
    }

    return result;
  }

  /**
   * Deletes a role from the database.
   * @param the_id The ID of the role.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean deleteRole(final long the_id) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element element = null;
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_id) {
        element = e.detach();
        result = true;
        break;
      }
    }
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(element);
      }
    }

    return result;
  }

  /**
   * Translates the XML element data into a Role object.
   * 
   * @param the_element The element that contains Role data.
   * @return a Role object.
   */
  private Role dataToRole(final Element the_element) {
    final long id = Long.parseLong(the_element.getAttributeValue(ATTRIBUTE_ID));
    final int role_id = Integer.parseInt(the_element.getAttributeValue(ATTRIBUTE_ROLE_ID));
    final long user_id = Long.parseLong(the_element.getChild(NODE_USER_ID).getText());
    final long conf_id = Long.parseLong(the_element.getChild(NODE_CONFERENCE_ID).getText());
    final Conference conf = MainPage.getDatabase().accessConf().getConference(conf_id);
    Role role = null;
    switch(role_id) {
      case Standards.ROLE_ID_PROGRAM_CHAIR:
        role = dataToPc(id, user_id, conf, the_element);
        break;
      case Standards.ROLE_ID_SUBPROGRAM_CHAIR:
        role = dataToSpc(id, user_id, conf, the_element);
        break;
      case Standards.ROLE_ID_AUTHOR:
        role = dataToAuthor(id, user_id, conf, the_element);
        break;
      case Standards.ROLE_ID_REVIEWER:
        role = dataToReviewer(id, user_id, conf, the_element);
        break;
      default:
    }
    
    return role;
  }

  /**
   * Translates the XML element data into a ProgramChair object.
   * 
   * @param the_id The ID for this particular role assignment.
   * @param the_user_id The ID of the User that this role belongs to.
   * @param the_conf The Conference that this role belongs to.
   * @param the_e The element that contains ProgramChair data.
   * @return a ProgramChair object.
   */
  private ProgramChair dataToPc(final long the_id,
                                final long the_user_id,
                                final Conference the_conf,
                                final Element the_e) {
    final Element node_spc = the_e.getChild(NODE_SPC);
    final List<Element> node_spcs = node_spc.getChildren();
    final List<Long> long_spcs = new ArrayList<Long>(node_spcs.size()); 
    for (Element e : node_spcs) {
      long_spcs.add(Long.parseLong(e.getText()));
    }
    
//    final Element node_manuscript = the_e.getChild(NODE_MANUSCRIPT);
//    final List<Element> node_manus = node_manuscript.getChildren();
    final List<Long> long_manus = new ArrayList<Long>();
//    for (Element e : node_manus) {
//      long_manus.add(Long.parseLong(e.getText()));
//    }
    
    return new ProgramChair(the_id, the_user_id, the_conf, long_spcs, long_manus);
  }

  /**
   * Translates the XML element data into a SubprogramChair object.
   * 
   * @param the_id The ID for this particular role assignment.
   * @param the_user_id The ID of the User that this role belongs to.
   * @param the_conf The Conference that this role belongs to.
   * @param the_e The element that contains SubprogramChair data.
   * @return a SubprogramChair object.
   */
  private SubprogramChair dataToSpc(final long the_id,
                                    final long the_user_id,
                                    final Conference the_conf,
                                    final Element the_e) {
    final Element node_pc = the_e.getChild(NODE_PC_ID);
    final long pc_id = Long.parseLong(node_pc.getText());
    
    final Element node_rev = the_e.getChild(NODE_REVIEWER);
    final List<Element> node_revs = node_rev.getChildren();
    final List<Long> long_revs = new ArrayList<Long>(node_revs.size()); 
    for (Element e : node_revs) {
      long_revs.add(Long.parseLong(e.getText()));
    }
    
//    final Element node_manuscript = the_e.getChild(NODE_MANUSCRIPT);
//    final List<Element> node_manus = node_manuscript.getChildren();
    final List<Long> long_manus = new ArrayList<Long>();
//    for (Element e : node_manus) {
//      long_manus.add(Long.parseLong(e.getText()));
//    }
    
    return new SubprogramChair(the_id,
                               the_user_id,
                               the_conf.getID(),
                               pc_id,
                               long_revs,
                               long_manus);
  }

  /**
   * Translates the XML element data into a Author object.
   * 
   * @param the_id The ID for this particular role assignment.
   * @param the_user_id The ID of the User that this role belongs to.
   * @param the_conf The Conference that this role belongs to.
   * @param the_e The element that contains Author data.
   * @return an Author object.
   */
  private Author dataToAuthor(final long the_id,
                              final long the_user_id,
                              final Conference the_conf,
                              final Element the_e) {
//    final Element node_manuscript = the_e.getChild(NODE_MANUSCRIPT);
//    final List<Element> node_manus = node_manuscript.getChildren();
    final List<Long> long_manus = new ArrayList<Long>();
//    for (Element e : node_manus) {
//      long_manus.add(Long.parseLong(e.getText()));
//    }
    return new Author(the_id, the_user_id, the_conf.getID(), long_manus);
  }

  /**
   * Translates the XML element data into a Reviewer object.
   * 
   * @param the_id The ID for this particular role assignment.
   * @param the_user_id The ID of the User that this role belongs to.
   * @param the_conf The Conference that this role belongs to.
   * @param the_e The element that contains Reviewer data.
   * @return a ProgramChair object.
   */
  private Reviewer dataToReviewer(final long the_id,
                                  final long the_user_id,
                                  final Conference the_conf,
                                  final Element the_e) {
    final Element node_spc = the_e.getChild(NODE_SPC_ID);
    final long long_spc = Long.parseLong(node_spc.getText());
    
//    final Element node_manuscript = the_e.getChild(NODE_MANUSCRIPT);
//    final List<Element> node_manus = node_manuscript.getChildren();
    final List<Long> long_manus = new ArrayList<Long>();
//    for (Element e : node_manus) {
//      long_manus.add(Long.parseLong(e.getText()));
//    }
    
    return new Reviewer(the_id, the_user_id, the_conf.getID(), long_spc, long_manus);
  }

  /**
   * Translates the Role object into XML element data.
   * 
   * @param the_role The Role object to translate.
   * @param the_edit True if this is an edit operation; false if it is a new
   *          entry.
   * @return an XML element node.
   */
  private Element roleToData(final Role the_role, final boolean the_edit) {
    final Element usernode = new Element(NODE_ID);

    usernode.setAttribute(ATTRIBUTE_ID, Long.toString(the_role.getID()));
    final int role_id = (int) the_role.getRoleID();
    usernode.setAttribute(ATTRIBUTE_ROLE_ID, Integer.toString(role_id));
    
    final long user_id = the_role.getUser().getID();
    usernode.addContent(new Element(NODE_USER_ID).setText(Long.toString(user_id)));
    final long conf_id = the_role.getConferenceID();
    usernode.addContent(new Element(NODE_CONFERENCE_ID).setText(Long.toString(conf_id)));
    
    switch(role_id) {
      case Standards.ROLE_ID_PROGRAM_CHAIR:
        usernode.addContent(pcToData((ProgramChair) the_role));
        break;
      case Standards.ROLE_ID_SUBPROGRAM_CHAIR:
        usernode.addContent(spcToData((SubprogramChair) the_role));
        break;
      case Standards.ROLE_ID_AUTHOR:
        usernode.addContent(authorToData((Author) the_role));
        break;
      case Standards.ROLE_ID_REVIEWER:
        usernode.addContent(reviewerToData((Reviewer) the_role));
        break;
      default:
    }

    return usernode;
  }

  /**
   * Translates the ProgramChair object into XML element data.
   * 
   * @param the_pc The Role object to translate.
   * @return A list of XML elements.
   */
  private List<Content> pcToData(final ProgramChair the_pc) {
    final List<Content> list = new ArrayList<Content>();
    
    final List<Long> spcs = the_pc.getSubprogramChairs();
    final Element node_spc = new Element(NODE_SPC);
    for (Long l : spcs) {
      node_spc.addContent(new Element(NODE_SPC_ID).setText(Long.toString(l)));
    }
    
//    final List<Long> manuscripts = the_pc.getManuscripts();
//    final Element node_manu = new Element(NODE_MANUSCRIPT);
//    for (Long l : manuscripts) {
//      node_manu.addContent(new Element(NODE_MANUSCRIPT_ID).setText(Long.toString(l)));
//    }
    
    list.add(node_spc);
//    list.add(node_manu);
    
    return list;
  }

  /**
   * Translates the SubprogramChair object into XML element data.
   * 
   * @param the_spc The Role object to translate.
   * @return A list of XML elements.
   */
  private List<Content> spcToData(final SubprogramChair the_spc) {
    final List<Content> list = new ArrayList<Content>();
    
    final long pc_id = the_spc.getProgramChair();
    final Element node_spc = new Element(NODE_PC_ID).setText(Long.toString(pc_id));
    
    final List<Long> reviewers = the_spc.getReviewers();
    final Element node_rev = new Element(NODE_REVIEWER);
    for (Long l : reviewers) {
      node_rev.addContent(new Element(NODE_REVIEWER_ID).setText(Long.toString(l)));
    }
    
//    final List<Long> manuscripts = the_spc.getManuscripts();
//    final Element node_manu = new Element(NODE_MANUSCRIPT);
//    for (Long l : manuscripts) {
//      node_manu.addContent(new Element(NODE_MANUSCRIPT_ID).setText(Long.toString(l)));
//    }
    
    list.add(node_spc);
    list.add(node_rev);
//    list.add(node_manu);
    
    return list;
  }

  /**
   * Translates the Author object into XML element data.
   * 
   * @param the_author The Role object to translate.
   * @return A list of XML elements.
   */
  private List<Content> authorToData(final Author the_author) {
    final List<Content> list = new ArrayList<Content>();
    
//    final List<Long> manuscripts = the_author.getManuscripts();
//    final Element node_manu = new Element(NODE_MANUSCRIPT);
//    for (Long l : manuscripts) {
//      node_manu.addContent(new Element(NODE_MANUSCRIPT_ID).setText(Long.toString(l)));
//    }
//    
//    list.add(node_manu);
    
    return list;
  }

  /**
   * Translates the Reviewer object into XML element data.
   * 
   * @param the_rev The Role object to translate.
   * @return A list of XML elements.
   */
  private List<Content> reviewerToData(final Reviewer the_rev) {
    final List<Content> list = new ArrayList<Content>();

    final long pc_id = the_rev.getSubprogramChair();
    final Element node_spc = new Element(NODE_SPC_ID).setText(Long.toString(pc_id));
    
//    final List<Long> manuscripts = the_rev.getManuscripts();
//    final Element node_manu = new Element(NODE_MANUSCRIPT);
//    for (Long l : manuscripts) {
//      node_manu.addContent(new Element(NODE_MANUSCRIPT_ID).setText(Long.toString(l)));
//    }
    
    list.add(node_spc);
//    list.add(node_manu);
    
    return list;
  }

}
